#include "jzbus.hpp"
#include "net_zbus_BusClient_BusClientUnsafe.h"
  
static zbuscli_t*
	get_client(JNIEnv* env, jobject obj);
static void 
	put_client(JNIEnv *env, jobject obj, zbuscli_t* cli);
static zbuscli_cfg_t*
	get_config(JNIEnv* env, jobject obj);


JNIEXPORT void JNICALL 
Java_net_zbus_BusClient_00024BusClientUnsafe_construct(
	JNIEnv* env, jobject obj, jobject cfg)
{ 
	zbuscli_t* cli = get_client(env, obj);
	if(cli) return;

	zbuscli_cfg_t* cli_cfg = get_config(env, cfg);
	
	cli = zbuscli_new(cli_cfg);
	assert(cli);

	put_client(env, obj, cli); 
 
	zbuscli_cfg_destroy(&cli_cfg); 
}

JNIEXPORT void JNICALL 
Java_net_zbus_BusClient_00024BusClientUnsafe_finalize(
	JNIEnv* env, jobject obj)
{
	zbuscli_t* cli = get_client(env, obj);
	assert(cli);
	zbuscli_destroy(&cli); 
}

JNIEXPORT jobjectArray JNICALL 
Java_net_zbus_BusClient_00024BusClientUnsafe_request
	(JNIEnv* env, jobject obj, jstring jsvc, jstring jtoken, jobjectArray jmsg)
{   
	zmsg_t* req = zmsg_new();
	jsize req_size = env->GetArrayLength(jmsg);
	for(int i=0; i<req_size; i++){
		jbyteArray frame = (jbyteArray)env->GetObjectArrayElement(jmsg, i);
		jbyte* data = env->GetByteArrayElements(frame, 0);
		jsize size = env->GetArrayLength(frame);
		assert(data); 
		zmsg_push_back(req, zframe_new(data, size));
		
		env->ReleaseByteArrayElements(frame, data, JNI_ABORT);
	} 

	const char* svc = env->GetStringUTFChars(jsvc, 0);
	const char* token = env->GetStringUTFChars(jtoken, 0);

	zbuscli_t* cli = get_client(env, obj);
	zmsg_t* res = zbuscli_request(cli, (char*)svc,(char*)token, req);

	env->ReleaseStringUTFChars(jsvc, svc);
	env->ReleaseStringUTFChars(jtoken, token);
	
	if(!res){
		raise_exception(env, "request result null");
		return NULL;
	}
	

	jsize res_size = zmsg_frame_size(res); 

	jclass cls_byte_array = env->FindClass("[B");
	assert(cls_byte_array);
	jobjectArray result = env->NewObjectArray(res_size, cls_byte_array, 0);
	env->DeleteLocalRef(cls_byte_array);

	 
	for(jsize i=0;i<res_size;i++){
		zframe_t* frame = zmsg_pop_front(res);
		jsize size = zframe_size(frame);
		jbyteArray jframe = env->NewByteArray(size); 
		env->SetByteArrayRegion(jframe, 0, size,(jbyte*) zframe_data(frame));
		env->SetObjectArrayElement(result, i, jframe);
		zframe_destroy(&frame);
	} 
	zmsg_destroy(&res);   
	return result;
}

static zbuscli_t*
get_client (JNIEnv* env, jobject obj)
{
	jclass cls = env->GetObjectClass (obj);
	assert (cls);
	jfieldID jhandle = env->GetFieldID (cls, "handle", "J");
	assert (jhandle);
	env->DeleteLocalRef (cls); 

	zbuscli_t* cli = (zbuscli_t*) env->GetLongField(obj, jhandle);
	return cli;
}
static void 
put_client (JNIEnv *env, jobject obj, zbuscli_t* cli)
{
	jclass cls = env->GetObjectClass (obj);
	assert (cls);
	jfieldID jhandle = env->GetFieldID (cls, "handle", "J");
	assert (jhandle);
	env->DeleteLocalRef (cls); 

	env->SetLongField(obj, jhandle, (jlong)cli);
}
 
static zbuscli_cfg_t*
get_config(JNIEnv* env, jobject obj){
	zbuscli_cfg_t* cfg = zbuscli_cfg_new();
	jclass cls = env->GetObjectClass (obj);
	assert (cls);

	//host & port
	jfieldID host_field = env->GetFieldID(cls, "host", "Ljava/lang/String;");
	assert (host_field);
	jstring jhost = (jstring)env->GetObjectField(obj, host_field);
	const char* host = env->GetStringUTFChars(jhost, 0); 
	jfieldID port_field = env->GetFieldID(cls, "port", "I");
	assert (port_field);
	int port = (int)env->GetIntField(obj, port_field);  
	zbuscli_cfg_set_broker(cfg, (char*)host, port);
	env->ReleaseStringUTFChars(jhost, host);

	//timeout
	jfieldID timeout_field = env->GetFieldID(cls, "timeout", "I");
	assert (timeout_field);
	int timeout = (int)env->GetIntField(obj, timeout_field);
	zbuscli_cfg_set_timeout(cfg, timeout);
	//retries
	jfieldID retries_field = env->GetFieldID(cls, "retries", "I");
	assert (retries_field);
	int retries = (int)env->GetIntField(obj, retries_field);
	zbuscli_cfg_set_retries(cfg, retries);
	//verbose
	jfieldID verbose_field = env->GetFieldID(cls, "verbose", "Z");
	assert (verbose_field);
	int verbose = (int)env->GetBooleanField(obj, verbose_field); 
	zbuscli_cfg_set_verbose(cfg, verbose); 

	//printf("%s:%d,%d,%d,%d\n",host, port, timeout, retries, verbose);

	env->DeleteLocalRef (cls);

	return cfg;
}
